﻿using System.Windows;
using System.Windows.Media;
using WickedFlame.Core.State;

namespace WickedFlame.Core.Shapes
{
	public class LineShape : VectorShape, IPolygonShape, IStateCapturable, ICopyable
	{
        public LineShape()
            : this(0, 0, 1, 0)
        {
        }

		public LineShape(double x1, double y1, double x2, double y2)
			: base()
		{
			_startPoint = new Point(x1, y1);
			_endPoint = new Point(x2, y2);
		}

        #region Properties

        public override Rect Bounds
        {
            get
            {
                Rect r = new Rect();
                if (StartPoint.X < EndPoint.X)
                {
                    if (StartPoint.Y < EndPoint.Y)
                    {
                        r = new Rect(StartPoint.X, StartPoint.Y, EndPoint.X - StartPoint.X, EndPoint.Y - StartPoint.Y);
                    }
                    else
                    {
                        r = new Rect(StartPoint.X, EndPoint.Y, EndPoint.X - StartPoint.X, StartPoint.Y - EndPoint.Y);
                    }
                }
                else
                {
                    if (StartPoint.Y < EndPoint.Y)
                    {
                        r = new Rect(EndPoint.X, StartPoint.Y, StartPoint.X - EndPoint.X, EndPoint.Y - StartPoint.Y);
                    }
                    else
                    {
                        r = new Rect(EndPoint.X, EndPoint.Y, StartPoint.X - EndPoint.X, StartPoint.Y - EndPoint.Y);
                    }
                }
                return r;
            }
        }

        Point _startPoint;
        public Point StartPoint
        {
            get
            {
                return _startPoint;
            }
            set
            {
                _startPoint = value;
                this.Invalidate();
            }
        }

        Point _endPoint;
        public Point EndPoint
        {
            get
            {
                return _endPoint;
            }
            set
            {
                _endPoint = value;
                this.Invalidate();
            }
        }

        public int X1
        {
            get
            {
                return (int)StartPoint.X;
            }
            set
            {
                _startPoint.X = value;
                this.Invalidate();
            }
        }

        public int Y1
        {
            get
            {
                return (int)StartPoint.Y;
            }
            set
            {
                _startPoint.Y = value;
                this.Invalidate();
            }
        }

        public int X2
        {
            get
            {
                return (int)EndPoint.X;
            }
            set
            {
                _endPoint.X = value;
                this.Invalidate();
            }
        }

        public int Y2
        {
            get
            {
                return (int)EndPoint.Y;
            }
            set
            {
                _endPoint.Y = value;
                this.Invalidate();
            }
        }

        public override int TrackerCount
        {
            get
            {
                return 2;
            }
        }

        #endregion

        #region Implementation

        public override void Invalidate()
        {
            base.Invalidate();

            NotifyPropertyChanged("StartPoint");
            NotifyPropertyChanged("EndPoint");
            NotifyPropertyChanged("X1");
            NotifyPropertyChanged("Y1");
            NotifyPropertyChanged("X2");
            NotifyPropertyChanged("Y2");

            DrawingCanvas.DrawTrackers();
        }

        public override void Draw(DrawingContext drawingContext)
        {
            double halfPenWidth = 0.5 / 2;

            // Create a guidelines set
            GuidelineSet guidelines = new GuidelineSet();
            guidelines.GuidelinesX.Add(Bounds.Left + halfPenWidth);
            guidelines.GuidelinesX.Add(Bounds.Right + halfPenWidth);
            guidelines.GuidelinesY.Add(Bounds.Top + halfPenWidth);
            guidelines.GuidelinesY.Add(Bounds.Bottom + halfPenWidth);

            drawingContext.PushGuidelineSet(guidelines);

            drawingContext.DrawLine(
                Pen /*new Pen(Stroke, StrokeThickness)*/,
                StartPoint,
                EndPoint);

            drawingContext.Pop();
        }

        public override void DrawTracker(DrawingContext drawingContext, double scale)
        {
            Pen p = new Pen(Brushes.Blue, 1 / scale);
            Pen ps = new Pen(Brushes.Black, 2 / scale);

            double r = 2.5 / scale;
            if (!(SelectedTracker == 1))
                drawingContext.DrawEllipse(Brushes.LightBlue, p, StartPoint, r, r);
            else
                drawingContext.DrawEllipse(Brushes.White, ps, StartPoint, r, r);

            if (!(SelectedTracker == 2))
                drawingContext.DrawEllipse(Brushes.LightBlue, p, EndPoint, r, r);
            else
                drawingContext.DrawEllipse(Brushes.White, ps, EndPoint, r, r);
        }

		/// <summary>
		/// Get handle point by 1-based number
		/// </summary>
        /// <param name="trackerNo"></param>
		/// <returns></returns>
        public override Point GetTracker(int trackerNo)
		{
            if (trackerNo == 1)
				return StartPoint;
			else
				return EndPoint;
		}

        ///// <summary>
        ///// Hit test
        ///// </summary>
        ///// <param name="point"></param>
        ///// <returns></returns>
        //public override int HitTestInt(Point point)
        //{
        //    if (IsSelected)
        //    {
        //        for (int i = 1; i <= TrackerCount; i++)
        //        {
        //            if (GetTrackerRectangle(i).Contains(point))
        //                return i;
        //        }
        //    }

        //    if (PointInObject(point))
        //        return 0;

        //    return -1;
        //}

        protected override bool PointInObject(Point point)
        {
            HitTestResult res = this.HitTest(point);
            if (res != null)
                return true;
            //res.
            return false;
            //return DefiningGeometry.StrokeContains(new Pen(this.Stroke, this.StrokeThickness), point);
        }

		public override bool IntersectsWith(Rect rectangle)
        {
            var line = new LineGeometry(StartPoint,EndPoint);
            IntersectionDetail det = line.StrokeContainsWithDetail(Pen /*new Pen(this.Stroke, this.StrokeThickness)*/, new RectangleGeometry(rectangle, 0, 0));

            if (det == IntersectionDetail.FullyContains || det == IntersectionDetail.FullyInside || det == IntersectionDetail.Intersects)
                return true;

            return false;
        }

        public override System.Windows.Input.Cursor GetTrackerCursor(int trackerNo)
		{
            switch (trackerNo)
			{
				case 1:
				case 2:
					return System.Windows.Input.Cursors.SizeAll;
				default:
					return System.Windows.Input.Cursors.Arrow;
			}
		}

        public override void MoveTracker(Point point, int trackerNo)
		{
            if (trackerNo == 1)
				StartPoint = point;
			else
				EndPoint = point;

			this.Invalidate();

            CallChangedHandler();
		}

        public override void MoveTracker(int tracker, double deltaX, double deltaY)
        {
            if (tracker == 1)
                StartPoint = new Point(StartPoint.X + deltaX, StartPoint.Y + deltaY);
            else
                EndPoint = new Point(EndPoint.X + deltaX, EndPoint.Y + deltaY); ;

            this.Invalidate();

            CallChangedHandler();
        }

		public override void Move(double deltaX, double deltaY)
		{
			StartPoint = new Point(StartPoint.X + deltaX, StartPoint.Y + deltaY);
			EndPoint = new Point(EndPoint.X + deltaX, EndPoint.Y + deltaY);

            CallMovedHandler();
        }

        #endregion

        #region IPoligonShape Member

        public void AddShapeToPoligon(PolygonShape poligon)
        {
            if (SelectedTracker > 0)
            {
                if (SelectedTracker == 2)
                {
                    Point p1 = StartPoint;
                    Point p2 = EndPoint;

                    StartPoint = p2;
                    EndPoint = p1;
                }
            }
            else
                throw new ShapeException("No Tracker is isSelected");

            //poligon.AddSegment(new LineSegment(EndPoint, true), StartPoint);
            poligon.AddSegment(new LineSegment(EndPoint, true), EndPoint);
        }

        public PolygonShape CreatePolygon()
        {
            // If tracker is selected it would mean that the shape will be combined with another
            // Flip corners so that the selected sides will join
            if (SelectedTracker == 1)
            {
                Point p1 = StartPoint;
                Point p2 = EndPoint;

                StartPoint = p2;
                EndPoint = p1;
            }

            var polygon = new PolygonShape(StartPoint);
            polygon.AddLineSegment(EndPoint);
            polygon.Stroke = this.Stroke;
            polygon.StrokeThickness = this.StrokeThickness;
            polygon.Fill = this.Fill;

            return polygon;
        }

        #endregion
        
        #region IHistoryable Members

        public ShapeMemento CreateMementoState()
        {
            var memento = new LineMemento();
            memento.SaveProperties(this);

            return memento;
        }

        public void ResetState(ShapeMemento memento)
        {
            memento.ResetProperties(this);
            this.Invalidate();
        }

        #endregion

        #region ICopyable Members

        public VectorShape Copy()
        {
            var shape = new LineShape(StartPoint.X, StartPoint.Y, EndPoint.X, EndPoint.Y);
            shape.Fill = this.Fill;
            shape.Stroke = this.Stroke;
            shape.StrokeThickness = this.StrokeThickness;

            return shape;
        }

        #endregion
    }
}
